import { Component, _decorator, js } from "cc";

"use strict";
let old = _decorator.ccclass;
const __TOOL_CACHE_KEY__ = '__TOOL_CACHE_KEY__';
const CACHE_KEY = '__ccclassCache__';
if (!_decorator["isNewClass"]) {
    _decorator.ccclass(function (ctor, name) {
        if(ctor && typeof(ctor) == "function" && ctor[CACHE_KEY]){
            ctor[__TOOL_CACHE_KEY__] = ctor[CACHE_KEY];
        }
        let ret = old.call(this, ctor, name);
        return ret;
    })
}
// npm i --save-dev @types/node
_decorator["isNewClass"] = true;

import {fs} from "fire-fs";
/** 自动代码使用格式 txtName、lbName、btnExample */
let nameStyle: any = {
    n: "cc.Node",
    nd: "cc.Node",
    box: "cc.Node",
    btn: "cc.Node",
    t: "cc.Label",
    lb: "cc.Label",
    txt: "cc.Label",
    img: "cc.Sprite",
    scroll: "cc.ScrollView",
    list: "List",
    rt: "cc.RichText",
    rtxt: "cc.RichText",
    rts: "RichTextSetter",
    edit: "cc.EditBox",
    pb: "cc.ProgressBar",
    bar: "cc.ProgressBar",
    sp: "sp.Skeleton",
    anim: "cc.Animation",
    ly: "cc.Layout",
    layout: "cc.Layout",
    slider: "cc.Slider",
    tg: "cc.Toggle",
    toggle: "cc.Toggle",
    tgctn: "cc.ToggleContainer",
    gp: "cc.Graphics",
    graphics: "cc.Graphics",
};
let replace = '\n\
    @property({type}) {name}: {type} = null;\
';
const PattStart = '===========================自动绑定代码开始===========================';
const PattEnd = '===========================自动绑定代码结束===========================';
let ChildrenMap = new Map();
/**
 * @en Registration method for the main process of Extension
 * @zh 为扩展的主进程的注册方法
 */
export const methods: { [key: string]: (...any: any) => any } = {
    /**
     * @en A method that can be triggered by message
     * @zh 通过 message 触发的方法
     */
    showLog() {
        console.log('Hello World');
    },

    onAutoCode(funName) {
        console.log("autoCode->onAutoCode")
        let list = Editor.Selection.getSelected("node");
        if (list) {
            console.log("============当前选中节点数:" + list.length);
            for (let i = 0; i < list.length; i++) {
                let uuid = list[i];
                console.log(i + "============", uuid);
            }
        }
    },

    onAutoBinder(funName) {
        console.log("autoBinder->onAutoBinder")
        let list = Editor.Selection.getSelected("node");
        if (list) {
            console.log("============当前选中节点数:" + list.length);
            for (let i = 0; i < list.length; i++) {
                let uuid = list[i];
                console.log(i + "============", uuid);
            }
        }
    },
};
//#region node-binder
function binderNodes(evt, uuid) {
    console.log("binderNodes " + uuid);
    let node = engine.getInstanceById(uuid);
    if (!node) {
        console.error("没有找到需要的节点");
        return;
    }
    // 缓存所有子节点
    cacheChildren(node);
    // 遍历可用自动绑定节点
    binder(node);
}
function autoCode(evt, uuid) {
    let node = engine.getInstanceById(uuid);
    if (!node) {
        console.error("没有找到需要的节点");
        return;
    }
    let findCls = false;
    let coms = node.getComponents(Component);
    let failList = [];
    for (let com of coms) {
        let name = js.getClassName(com);
        if (checkScript(name)) {
            findCls = true;
            // 缓存所有子节点
            cacheChildren(node);

            // 寻找脚本路径 打开文本
            let comUuid = com.__scriptUuid;
            // console.log("uuid: " + comUuid);
            let path = Editor.remote.assetdb.uuidToFspath(comUuid);
            let txt = fs.readFileSync(path, "utf-8");
            // console.log(txt);
            // 正则提取1
            // let patt = /===========================自动生成开始===========================/igm
            let patt = new RegExp(`${PattStart}[\\s\\S]*${PattEnd}\*`, "gm");
            let res = txt.match(patt);
            if (res) {
                console.log(res);
                console.log("脚本: " + name);
            }
            else {
                console.log(`=====${name}脚本中没有发现替代注释  已自动生成以下注释======`);
                console.log(`/*${PattStart}*/`);
                console.log(`/*${PattEnd}*/`);
                let clsPatt = `([\\s]class[\\s]${name}[\\s]extends[\\s].*?\\{)`;
                res = txt.match(new RegExp(clsPatt, "g"));
                if (res) {
                    let autoGenStr = `${res}\n\t/*${PattStart}*/\n\t/*${PattEnd}*/`
                    txt = txt.replace(res.toString(), autoGenStr);
                    fs.writeFileSync(path, txt);
                    console.log(`匹配class ${name}成功，自动插入注释，开始生成代码`);
                    res = txt.match(patt);//自动插入注释后，重新匹配
                    if (!res) {
                        console.error(`生成代码失败-检查自动插入注释是否异常`);
                        return;
                    }
                    else {
                        console.log(res);
                        console.log("脚本: " + name);
                    }
                }
                else {
                    console.error(`匹配class ${name}失败，请手动插入注释代码`);
                    return;
                }
            }
            let str = `${PattStart}*/`;
            ChildrenMap.forEach((childs, key) => {
                if (childs) {
                    let child = childs[0];
                    let childName = child.name;
                    let type = capitalType(childName)
                    if (!type) {
                        return;
                    }
                    if (childs.length > 1) {
                        failList.push(`${childName}生成失败，存在${childs.length}个重名节点type:${type}`);
                        return
                    }
                    else {
                        console.log(childName);
                    }
                    // 有定义此组件则替换
                    let txmp = replace;
                    if (type) {
                        let name = child.name;
                        txmp = txmp.replace("{type}", type);
                        txmp = txmp.replace("{type}", type);
                        txmp = txmp.replace("{name}", name);
                    }
                    str += txmp;
                }
            });
            str += "\n\t/*" + PattEnd + "";
            res.toString();
            txt = txt.replace(res.toString(), str);
            fs.writeFileSync(path, txt);
            let assetPath = Editor.remote.assetdb.uuidToUrl(comUuid);
            // Editor.Ipc.sendToMain('assets:open-text-file', comUuid);
            // createOrSave(url: string, data: string, cb?: (err: any, result: any) => void): void;
            Editor.assetdb.refresh(assetPath, (err, result) => {
                console.log("refresh complete", result)
                // binderNodes(evt, uuid);
            }, () => {
                console.log("refresh test")
            });
            // setTimeout(() => {
            //     binderNodes(evt, uuid);
            // }, 5000);
            break;
        }
    }
    if (!findCls) {
        Editor.warn(`TODO--暂未实现自动创建代码类--->选中节点未挂载脚本后重试`);
    }
    for (let i = 0; i < failList.length; i++) {
        Editor.warn(failList[i]);
    }
}
/**
 * 遍历可用自动绑定节点
 * @param node
 * @returns
 */
function binder(node) {
    let findCls = false;
    let coms = node.getComponents(Component);
    for (let com of coms) {
        let name = js.getClassName(com);
        if (checkScript(name)) {
            findCls = true;
            let cache = com.__proto__.constructor[__TOOL_CACHE_KEY__];
            if (!cache) {
                Editor.warn(`====${name}绑定失败，未找到缓存节点==========`);
                return;
            }
            let properties = cache.proto.properties;
            for (let key in properties) {
                // 检测脚本是否有此属性 并且未绑定脚本
                if (com[key] === null) {
                    // 检测是否有名字相同的子节点并且 有相同需要的组件
                    let data = properties[key];
                    let children = ChildrenMap.get(key);
                    if (children) {
                        let nodes = children.filter(node => {
                            if (data.type == Node) {
                                return true;
                            }
                            return node.getComponent(data.type);
                        });
                        if (nodes && nodes.length) {
                            let first = nodes[0];
                            let needCom = null;
                            if (data.type == Node) {
                                needCom = first;
                            }
                            else {
                                needCom = first.getComponent(data.type);
                            }
                            com[key] = needCom;
                            continue;
                        }
                        else {
                            Editor.warn(`${key}没有对应的组件type:${data.type}`);
                        }
                    }
                    else {
                        Editor.warn(`${key} 没有此节点`);
                    }
                }
            }
            // com.__proto__.constructor[__TOOL_CACHE_KEY__] = undefined;
        }
    }
    if (!findCls) {
        Editor.warn(`绑定失败，选中节点${node.name}未挂载脚本`);
    }
}

function cacheChildren(node) {
    ChildrenMap = new Map();
    _cacheChildren(node, ChildrenMap);
}
function _cacheChildren(node, cMap) {
    var _aList;
    let children = node.children;
    for (let i = 0; i < children.length; i++) {
        let child = children[i];
        _aList = cMap.get(child.name);
        if (!_aList) {
            _aList = [];
            cMap.set(child.name, _aList);
        }
        _aList.push(child);
        if (child.childrenCount > 0) {
            let coms = child.getComponents(Component);
            let canCache = true;
            let compName = "";
            for (let com of coms) {
                compName = js.getClassName(com);
                if (checkScript(compName)) {
                    //孙节点数量>0(child.childrenCount)的子节点(child)存在用户脚本，不能生成到父节点(node)中的用户脚本
                    canCache = false;
                    break;
                }
            }
            if (canCache) {
                _cacheChildren(child, cMap);
            }
            else {
                if (compName != "List") {
                    //List的子节点不自动生成，且无需提示
                    setTimeout(() => {
                        //加延时，让控制台信息在最后打印，方便查看
                        Editor.warn(`${child.name}子节点存在独立脚本${compName}，请单独生成绑定`);
                    }, 100);
                }
            }
        }
    }
}

/**检测是否可生成代码到对应的compName中 */
function checkScript(compName) {
    return !compName.startsWith("cc.") && compName != "RenderList" && compName != "ListItem";
}


function capitalType(name) {
    if (!name) return null;
    let uIndex = -1;
    for (let i = 0, length = name.length; i < length; i++) {
        const code = name.charCodeAt(i);
        if (code < 97 || code > 122) {
            if (i == 0) {
                // 第一个字符不是小写字母
                return null;
            }

            const isUpper = code > 64 && code < 91;
            // 大写字母、数字、$、_
            const isLegalChar = isUpper || (code > 47 && code < 58) || code == 36 || code == 95;

            if (!isLegalChar) {
                // 不是合法字符
                return null;
            }

            if (uIndex == -1) {
                if (isUpper) {
                    uIndex = i;
                } else {
                    // 未出现大写字母，非法
                    return null;
                }
            }
        }
    }

    if (uIndex > 0) {
        const type = name.slice(0, uIndex);
        if (nameStyle[type]) {
            return nameStyle[type];
        }
    }
    return null;
}
//#endregion
/**
 * @en Method Triggered on Extension Startup
 * @zh 扩展启动时触发的方法
 */
export function load() { }

/**
 * @en Method triggered when uninstalling the extension
 * @zh 卸载扩展时触发的方法
 */
export function unload() { }
